package com.itheima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.common.constants.BusinessConstants;
import com.itheima.common.enums.HttpCodeEnum;
import com.itheima.common.exception.LeadNewsException;
import com.itheima.common.util.RequestContextUtil;
import com.itheima.common.vo.PageResultVo;
import com.itheima.wemedia.dto.WmNewsContentNodeDto;
import com.itheima.wemedia.dto.WmNewsDtoSave;
import com.itheima.wemedia.dto.WmNewsPageReqDto;
import com.itheima.wemedia.mapper.WmMaterialMapper;
import com.itheima.wemedia.mapper.WmNewsMaterialMapper;
import com.itheima.wemedia.mapper.WmUserMapper;
import com.itheima.wemedia.pojo.WmMaterial;
import com.itheima.wemedia.pojo.WmNews;
import com.itheima.wemedia.mapper.WmNewsMapper;
import com.itheima.wemedia.pojo.WmNewsMaterial;
import com.itheima.wemedia.pojo.WmUser;
import com.itheima.wemedia.service.WmNewsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.wemedia.vo.WmNewsVo;
import io.swagger.models.auth.In;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @description <p>自媒体图文内容信息 业务实现</p>
 *
 * @version 1.0
 * @package com.itheima.wemedia.service.impl
 */
@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmUserMapper wmUserMapper;
    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Override
    public PageResultVo<WmNewsVo> findPageAdmin(WmNewsPageReqDto dto) {
        //构建分页
        IPage<WmNews> pageInfo = new Page<>(dto.getPage(),dto.getSize());
        //
        LambdaQueryWrapper<WmNews> lqw = new LambdaQueryWrapper<>();
        lqw.eq(null!=dto.getStatus(),WmNews::getStatus,dto.getStatus());
        lqw.like(null!=dto.getTitle(),WmNews::getTitle,dto.getTitle());

        page(pageInfo,lqw);
        //根据查询到文章集合获取userId,遍历查询作者名，并封装
        //根据id查询自媒体用户
        List<Integer> userIdList = pageInfo.getRecords().stream().map(WmNews::getUserId).collect(Collectors.toList());
        //在根据id集合批量查询用户
        List<WmUser> wmUsers = wmUserMapper.selectBatchIds(userIdList);
        //将用户id和作者名即用户名组成map集合
        Map<Integer, WmUser> userNameMap = wmUsers.stream().collect(Collectors.toMap(WmUser::getId, Function.identity()));
        //将用户名组装
        List<WmNewsVo> list = pageInfo.getRecords().stream().map(wmNews -> WmNewsVo.build(wmNews,userNameMap.get(wmNews.getUserId()))).collect(Collectors.toList());


        return PageResultVo.pageResult(pageInfo.getCurrent(),pageInfo.getSize(),pageInfo.getTotal(),list);
    }
    /**
     * 文章审核驳回
     *
     * @param paramMap
     */
    @Override
    public void authFail(Map<String, Object> paramMap) {
        WmNews wmNews = new WmNews();
        wmNews.setReason(((String) paramMap.get("msg")));
        wmNews.setStatus(BusinessConstants.WmNewsConstants.STATUS_BLOCK);
        wmNews.setId((Long)(paramMap.get("id")));
        updateById(wmNews);
    }

    /**
     * 文章人工审核通过
     *
     * @param id
     */
    @Override
    public void authPass(Integer id) {
        WmNews wmNews = new WmNews();
        wmNews.setStatus(BusinessConstants.WmNewsConstants.STATUS_MANUAL_PASS);
        wmNews.setId(Long.valueOf(id));
        updateById(wmNews);
    }

    /**
     * 查询审核通过的文章，状态为8。进行数据同步到文章微服务
     * @param dto
     * @return
     */
    @Override
    public List<WmNewsVo> list4ArticleSync(WmNewsPageReqDto dto) {
        //查询状态为8的文章,并且发布时间小于当前时间
//        List<WmNews> wmNewsList = query().eq("status", dto.getStatus()).list();
        LambdaQueryWrapper<WmNews> lqw = new LambdaQueryWrapper<>();
        lqw.eq(null!=dto.getStatus(),WmNews::getStatus,dto.getStatus());
        //发布时间可能为空,所以需要在发布文章时，添加当前的发布时间
        //判断发布时间是否小于当前时间
        lqw.le(null!=dto.getEndPubDate(),WmNews::getPublishTime,dto.getEndPubDate());
        List<WmNews> wmNewsList = wmNewsMapper.selectList(lqw);
        //获取所有的用户id
        List<WmNewsVo> wmNewsVoList = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(wmNewsList)){
            List<Integer> userIds = wmNewsList.stream().map(WmNews::getUserId).collect(Collectors.toList());
            //根据userId查询作者名称，即用户名
            List<WmUser> wmUsers = wmUserMapper.selectBatchIds(userIds);
            //将用户id和名称组成map
            Map<Integer,WmUser> wmInfoMap = wmUsers.stream().collect(Collectors.toMap(WmUser::getId, Function.identity()));
            log.debug("===================wmInfoMap"+wmInfoMap);
            //遍历名称重新组装wmNewsVo
            wmNewsVoList = wmNewsList.stream().map(wmNews -> WmNewsVo.build(wmNews,wmInfoMap.get(wmNews.getUserId()))).collect(Collectors.toList());
        }

////        优化
//        List<WmNewsVo> wmNewsVoList = new ArrayList<>();
//        for (WmNews wmNews : wmNewsList) {
//            WmNewsVo wmNewsVo = new WmNewsVo();
//            BeanUtils.copyProperties(wmNews,wmNewsVo);
//            wmNewsVo.setAuthorName(wmInfoMap.get(wmNews.getUserId()));
//            wmNewsVoList.add(wmNewsVo);
//        }
        return wmNewsVoList;
    }

    /**
     * 文章发布条件查询
     *
     * @param dto
     * @return
     */
    @Override
    public PageResultVo<WmNews> findPage(WmNewsPageReqDto dto) {
        //1. 设置分页参数
        IPage<WmNews> pageParam = new Page(dto.getPage(), dto.getSize());
        //2. 构建分页条件
        LambdaQueryWrapper<WmNews> queryWrapper = Wrappers.<WmNews>lambdaQuery();
        //前端回来的参数不一致，获取全部没有状态即
        queryWrapper.eq(null != dto.getStatus(), WmNews::getStatus, dto.getStatus())
                .eq(WmNews::getUserId, RequestContextUtil.getUserId())
                .eq(null != dto.getChannelId(), WmNews::getChannelId, dto.getChannelId())
                .ge(null != dto.getBeginPubDate(), WmNews::getPublishTime, dto.getBeginPubDate())
                .lt(null != dto.getEndPubDate(), WmNews::getPublishTime, dto.getEndPubDate())
                .like(null != dto.getKeyword(), WmNews::getTitle, dto.getKeyword())
                .orderByDesc(WmNews::getCreatedTime);
        //3. 分页查询
        IPage pageInfo = page(pageParam, queryWrapper);
        //4. 构建分页结果并返回
        return PageResultVo.pageResult(dto.getPage(), dto.getSize(), pageInfo.getTotal(), pageInfo.getRecords());
    }



    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * 发表和更新文章
     *
     * @param draft
     * @param dtoSave
     * @return
     */
    @Override
    public Integer submit(Boolean draft, WmNewsDtoSave dtoSave) throws LeadNewsException {
        System.out.println("draft = " + draft);
        LocalDateTime now = LocalDateTime.now();
        // 存储封面的图片
        List<String> coverImageList = new ArrayList<>();
        // 存在内容图片
        List<String> contentImageList = new ArrayList<>();
        // 参数校验:
        // 文章内容
        String content = dtoSave.getContent();
        if(null == dtoSave|| StringUtils.isEmpty(content)){
            throw new LeadNewsException("文章内容不能为空!");
        }
        //1. 添加数据到文章表wm_news
        WmNews pojo = new WmNews();
        // 复制对象中的属性：属性名称且类型相同时才会复制
        BeanUtils.copyProperties(dtoSave, pojo);
        // 发布者
        pojo.setUserId(Integer.parseInt(RequestContextUtil.getUserId()));
        pojo.setSubmitedTime(now);

        // 是否是草稿
        //Boolean isDraft = Boolean.valueOf(draft);
        pojo.setStatus(draft? BusinessConstants.WmNewsConstants.STATUS_DRAFT:BusinessConstants.WmNewsConstants.STATUS_SUBMIT);
        // 刚刚创建的文章，没上架
        pojo.setEnable(BusinessConstants.WmNewsConstants.ENABLE_OFF);
        // 文章封面, 如果是自动的。dto提交过来的images是空的
        if(CollectionUtils.isNotEmpty(dtoSave.getImages())){
            // 有图片，且是多图时，以逗号分割拼接
            //String.join(",", dtoSave.getImages()), 把参数2集合,拼接成字符串，每个元素以参数1（,）分割
            String images = String.join(",", dtoSave.getImages());
            pojo.setImages(images);
            // 添加封面图片
            coverImageList.addAll(dtoSave.getImages());
        }

        //从文章内容中抽取图片作为封面图片
        List<WmNewsContentNodeDto> contentNodeList = JSON.parseArray(content, WmNewsContentNodeDto.class);
        //List<String> collect = contentNodeList.stream()
        //    .filter(node -> BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(node.getType()))
        //    .map(WmNewsContentNodeDto::getValue).collect(Collectors.toList());
        // 抽取图片: 遍历contentNodeList, 如果type="image", 提取value
        // 内容中的图片
        for (WmNewsContentNodeDto node : contentNodeList) {
            // 这个节点就是图片
            if(BusinessConstants.WmNewsConstants.CONTENT_IMAGE.equals(node.getType())){
                String imageUrl = node.getValue();
                contentImageList.add(imageUrl);
            }
        }
        if(dtoSave.getType().intValue() == BusinessConstants.WmNewsConstants.TYPE_AUTO){
            // 封面类型为自动
            /**
             * -1 是自动: 从文章内容中抽取图片作为封面图片
             *    文章中没图片：type=0
             *    图片小于2张: type=1
             *    图片超过2张: type=3
             */
            int size = contentImageList.size();
            //文章中没图片：type=0
            if(size == 0){
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_NONE_IMG);
                // 没图，封面就空白
                pojo.setImages("");
            }else if (size <= 2) {
                //图片小于2张: type=1
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_SINGLE_IMG);
                // 添加封面图片
                pojo.setImages(contentImageList.get(0));
                coverImageList.add(contentImageList.get(0));
            }else{
                //图片超过2张: type=3
                pojo.setType(BusinessConstants.WmNewsConstants.TYPE_MULTIPLE_IMG);
                // 取前3张图片   limit 0,size, 循环3次
                StringJoiner joiner = new StringJoiner(",");
                //String images = "";
                for (int i = 0; i < 3; i++) {
                    //images+=contentImageList.get(i) + ",";
                    joiner.add(contentImageList.get(i));
                    coverImageList.add(contentImageList.get(i));
                }
                String images = joiner.toString();
                // 设置文章的封面图片
                pojo.setImages(images);
            }


        }

        //因为修改的数据使用的是同一个地址
//        所以根据前端传回的数据中判断是否有id,来判断是否为添加还是更新
        if (null!= dtoSave.getId()){
            //更新操作
            removeWmNewsMaterialRelationship(dtoSave.getId());
            updateById(pojo);
        }else {
            // 保存到数据
            pojo.setCreatedTime(now);
            save(pojo);


        }

        //2. 添加文章与素材的关系
        //2.1 获取封面图片list, 内容图片list
        saveWmNewsMaterialRelationship(pojo.getId().intValue(),coverImageList, contentImageList);
        //发送消息到kafaka
        if(pojo.getStatus()==BusinessConstants.WmNewsConstants.STATUS_SUBMIT){
            //自动审核
            kafkaTemplate.send(BusinessConstants.MqConstants.WM_NEWS_AUTO_SCAN_TOPIC,pojo.getId().toString());
        }
        return pojo.getId().intValue();
    }

    /**
     * 添加文章与图片的关系
     * @param newsId
     * @param coverImageList
     * @param contentImageList
     */
    private void saveWmNewsMaterialRelationship(Integer newsId, List<String> coverImageList, List<String> contentImageList) {
        // 合并图片集合且去重
        Set<String> allImages = new HashSet<String>();
        if(CollectionUtils.isNotEmpty(coverImageList)){
            // 去除集合中每个图片地址中的前缀
            coverImageList = coverImageList.stream().map(this::removePrefix).collect(Collectors.toList());
            allImages.addAll(coverImageList);
        }
        // 内容中的图片处理
        if(CollectionUtils.isNotEmpty(contentImageList)){
            // 图片地址截取去掉前缀，得到FileID
            contentImageList = contentImageList.stream().map(this::removePrefix).collect(Collectors.toList());
            allImages.addAll(contentImageList);
        }
        // 通过FileID查询素材表获取素材表中的id
        if(CollectionUtils.isNotEmpty(allImages)) {
            // 构建批量查询条件
            LambdaQueryWrapper<WmMaterial> queryWrapper = Wrappers.<WmMaterial>lambdaQuery().in(WmMaterial::getUrl, allImages);
            // 通过url批量查询 allImages.size=3,  wmMaterials.size()=2
            List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(queryWrapper);
            if(null==wmMaterials || allImages.size() != wmMaterials.size()){
                throw new LeadNewsException("文章素材缺失");
            }
            // 添加素材(id,url)与文章的关系
            Map<String, Integer> urlIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));

            // 1. 封面 url, 转成WmNewsMaterial对象(newsId, 素材id,1)
            if(CollectionUtils.isNotEmpty(coverImageList)){
                for (String url : coverImageList) {
                    // 获取素材的id
                    Integer materialId = urlIdMap.get(url);
                    WmNewsMaterial newsMaterial = new WmNewsMaterial();
                    newsMaterial.setMaterialId(materialId);
                    newsMaterial.setNewsId(newsId);
                    newsMaterial.setType(BusinessConstants.WmNewsConstants.WM_COVER_REFERENCE);
                    wmNewsMaterialMapper.insert(newsMaterial);
                }
            }
            // 2. 内容 url 转成WmNewsMaterial对象(newsId, 素材id,0)
            if(CollectionUtils.isNotEmpty(contentImageList)){
                for (String url : contentImageList) {
                    // 获取素材的id
                    Integer materialId = urlIdMap.get(url);
                    WmNewsMaterial newsMaterial = new WmNewsMaterial();
                    newsMaterial.setMaterialId(materialId);
                    newsMaterial.setNewsId(newsId);
                    newsMaterial.setType(BusinessConstants.WmNewsConstants.WM_CONTENT_REFERENCE);
                    wmNewsMaterialMapper.insert(newsMaterial);
                }
            }
            //mybatis批量添加
        }
    }

    private String removePrefix(String url){
        return url.replaceAll("http://((\\d){1,3}\\.){3}(\\d){1,3}:?(\\d){0,5}\\/", "");
    }
    private void removeWmNewsMaterialRelationship(Integer newsId){
        //通过文章id删除关联表里的数据
        wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery().eq(WmNewsMaterial::getNewsId,newsId));
    }


    /**
     * 删除文章
     * @param id
     * @return
     */
    @Override
    @Transactional
    public void deleteNews(Integer id) {
        //1.考虑文章是否上架(enable)和是否已发布的状态（status)
        //2.删除文章与素材的关系表
        //3.多张表的操作，加入事务控制
        WmNews wmNews = getById(id);
        if (wmNews.getEnable()==BusinessConstants.WmNewsConstants.ENABLE_ON || wmNews.getStatus()==BusinessConstants.WmNewsConstants.STATUS_PUBLISHED) {
            throw new LeadNewsException("该文章已上架或者已发布，不能删除");
        }else {
            //调用自定义方法删除文章素材表
            removeWmNewsMaterialRelationship(id);
            removeById(id);
        }
    }
    @Override
    public void down(Map<String,Integer> map){
        //参数校验
        Integer newId = map.get("id");
        Integer enable = map.get("enable");
        if(newId==null || null == enable || enable.intValue()<0 || enable.intValue()>1){
            throw new LeadNewsException(HttpCodeEnum.PARAM_INVALID);
        }
        WmNews wmNews=new WmNews();
        wmNews.setId(Long.valueOf(newId));
        if (enable==BusinessConstants.WmNewsConstants.ENABLE_ON){
            //可以上架
            wmNews = getById(newId);
            //判断是否为发布状态
            if (wmNews.getStatus()==BusinessConstants.WmNewsConstants.STATUS_PUBLISHED){
                //上架
                wmNews.setEnable(BusinessConstants.WmNewsConstants.ENABLE_ON);
            }
        }else {
            //下架需要将enable状态改为0,并且status改为1
            wmNews.setStatus(BusinessConstants.WmNewsConstants.STATUS_SUBMIT);
            wmNews.setEnable(BusinessConstants.WmNewsConstants.ENABLE_OFF);
        }
        updateById(wmNews);
    }

}
